package ferp.core.calc;

import ferp.core.calc.scorer.*;
import ferp.core.game.Game;
import ferp.core.game.Settings;
import ferp.core.player.Player;
import ferp.core.util.Convert;

/**
 * User: olegs
 * Date: 20/05/11 18:10
 */
public abstract class Calculator
{
  // get calculator instance by game flavor
  public static Calculator get(Settings.GameFlavor flavor) {return calculators[flavor.ordinal()];}

  // check if player is closed
  public abstract boolean closed(Game g, Settings settings, Player p);
  // check if game is over
  public abstract boolean over(Game g, Settings settings);
  // get mountain normalize factor
  public abstract int getNormalizeFactor();

  // get number of expected whists for open game type
  public static int getExpectedOpenWhists(int contract) {return expectedWhists[Convert.index(contract)][EXPECTED_OPEN_WHISTS];}
  // get number of expected whists for blind game type
  public static int getExpectedStandingWhists(int contract) {return expectedWhists[Convert.index(contract)][EXPECTED_STANDING_WHISTS];}
  // success/failure play and whists costs
  public int getPlayCostSuccess(int contract) {return costs[Convert.index(contract)][PLAY_SUCCESS];}
  public int getPlayCostFailure(int contract) {return costs[Convert.index(contract)][PLAY_FAIL];}
  public int getWhistCostSuccess(int contract) {return costs[Convert.index(contract)][WHISTS_SUCCESS];}
  public int getWhistCostFailure(int contract) {return costs[Convert.index(contract)][WHISTS_FAIL];}
  // get cost of missed trick (for declarer)
  public int getMissedTrickCost(int contract) {return costs[Convert.index(contract)][WHISTS_SUCCESS];}

  // write into player's mountain
  public void writeToMountain(Game g, Player p, long value) {g.getScore(p.id()).mountain.add(value);}
  // write into player's pool
  public void writeToPool(Game g, Settings settings, Player p, long value) {g.getScore(p.id()).pool.add(value);}
  // write whists on another player
  public void writeToWhists(Game g, Player from, Player to, long value) {g.getScore(from.id()).whists[to.id()].add(value);}

  // score passing set
  public void passing(Game g, Settings settings) {Passing.instance.score(g, settings, this);}
  // score misere set
  public void misere(Game g, Settings settings) {Misere.instance.score(g, settings, this);}
  // score tricking set
  public void tricking(Game g, Settings settings) {Tricking.instance.score(g, settings, this);}
  // score tricking with 2 passes set
  public void trickingTwoPasses(Game g, Settings settings) {TrickingTwoPasses.instance.score(g, settings, this);}
  // score tricking with 1 passer and 1 half-whister
  public void trickingHalfWhist(Game g, Settings settings) {TrickingHalfWhist.instance.score(g, settings, this);}
  // score tricking 'without 3'
  public void trickingWithout3(Game g, Settings settings) {TrickingWithout3.instance.score(g, settings, this);}
  // score checked 'tricking 10'
  public void tricking10(Game g, Settings settings) {Tricking10.instance.score(g, settings, this);}

  // positions in 'costs' table
  protected static final int PLAY_SUCCESS   = 0;
  protected static final int WHISTS_SUCCESS = 1;
  protected static final int PLAY_FAIL      = 2;
  protected static final int WHISTS_FAIL    = 3;

  // positions in 'expectedWhists' table
  protected static final int EXPECTED_OPEN_WHISTS = 0;
  protected static final int EXPECTED_STANDING_WHISTS = 1;

  // tricking/misere games costs (rows 0 to 4 represent games - for columns see positions above)
  protected int[][] costs = new int[5][];

  private static final int[][] expectedWhists =
      {
          {4, 2},
          {2, 1},
          {1, 1},
          {1, 1},
          {1, 1}
      };

  private static Calculator[] calculators = new Calculator[Settings.GameFlavor.values().length];

  static
  {
    calculators[Settings.GameFlavor.SOCHI.ordinal()] = new Sochi();
    calculators[Settings.GameFlavor.LENINGRAD.ordinal()] = new Leningrad();
    calculators[Settings.GameFlavor.ROSTOV.ordinal()] = new Rostov();
  }
}
